/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.util;

import java.io.Serializable;

/**A double precision, general, dynamically-resizable, one-dimensional vector class.
* Index numbering begins with zero
* @author Fonseca, Flavio & Pitangueira, Roque
* @since May 2004
*/
public class IVector implements Serializable, Cloneable {
	
	private static final long serialVersionUID = 1L;
	int size;
	double[] vector;
	
//*****************************************************************************
	
	/**Constructs a new IVector from the specified array elements.
	*@param vector the values for the new IVector.
	*/
	public IVector(double[] vector) {
		size = vector.length;
		this.vector = new double[size];
		for (int i=0; i<size; i++) {
			this.vector[i] = vector[i];
		}
	}
	
//*****************************************************************************
	
	/**Constructs a new IVector of the specified length and initializes it by copying the specified number of elements from the specified array.
	*@param vector The array from which the values will be copied.
	*@param length The number of values copied from the array.
	*/
	public IVector(double[] vector, int length) {
		size = length;
		this.vector = new double[length];
		for (int i=0; i<length; i++) {
			this.vector[i] = vector[i];
		}
	}
	
//*****************************************************************************
	
	/**Constructs a new IVector from the specified vector.
	*@param vector the source IVector for this new IVector
	*/
	public IVector(IVector vector) {
		size = vector.getSize();
		this.vector = new double[size];
		for (int i=0; i<size; i++)
			this.vector[i] = vector.getElement(i);
	}
	
//*****************************************************************************
	
	/**Constructs a new IVector of the specified length with all vector elements initialized to 0.
	*@param length the number of elements in this IVector.
	*/
	public IVector(int length) {
		size = length;
		this.vector = new double[length];
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to sum of itself and the specified vector.
	*@param vector the second vector
	*/
	public final void add(IVector vector) throws MismatchedSizeException{
		
		if (this.size != vector.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<this.size; i++) {
			this.vector[i] = this.vector[i] + vector.getElement(i);
		}
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the vector sum of vectors vector1 and vector2.
	*@param vector1 the first vector
	*@param vector2 the second vector
	*/
	public final void add(IVector vector1, IVector vector2) throws MismatchedSizeException{
		
		if (this.size != vector1.getSize()) {
			throw (new MismatchedSizeException());
		}
		if (this.size != vector2.getSize()) {
			throw (new MismatchedSizeException());
		}
		if (vector1.getSize() != vector2.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<this.size; i++) {
			this.vector[i] = vector1.getElement(i) + vector2.getElement(i);
		}
	}
	
//*****************************************************************************
	
	/**Creates a new object of the same class as this object.*/
	public Object clone() {
		IVector ivec = new IVector(this);
		return ivec;
	}
	
//*****************************************************************************
	
	/**Returns the dot product of this vector and vector v1.
	*@param v1 the other vector
	*@return the dot product of this and v1
	*/
	public double dot(IVector v1) {
		int i;
		double dot = 0;
		for(i=0; i<this.size; i++)
			dot = dot + this.vector[i] * v1.getElement(i);
		return (dot);
	}
	
//*****************************************************************************
	
	/**Returns true if all of the data members of IVector vector1 are equal to the corresponding data members in this IVector.
	*@param vector1 The vector with which the comparison is made
	*@return true or false
	*/
	public boolean equals(IVector vector1) {
		if (this.size != vector1.getSize())
			return false;
		for (int i=0; i<this.size; i++) {
			if (this.vector[i] != vector1.getElement(i))
				return false;
		}
		return true;
	}
	
//*****************************************************************************
	
	/**Returns true if the Object o1 is of type IVector and all of the data members of o1 are equal to the corresponding data members in this IVector.
	*@param o1 The object with which the comparison is made
	*@return true or false
	*/
	public boolean equals(Object o1) {
		if (o1 instanceof IVector) {
			if (this.size != ((IVector)o1).getSize())
				return false;
			for (int i=0; i<this.size; i++) {
				if (this.vector[i] != ((IVector)o1).getElement(i))
					return false;
			}
		}
		return true;
	}
	
//*****************************************************************************
	
	/**Retrieves the value at the specified index value of this vector.
	*@param index the index of the element to retrieve (zero indexed)
	*@return the value at the indexed element
	*/
	public final double getElement(int index) {
		return this.vector[index];
	}
	
//*****************************************************************************
	
	/**Returns the number of elements in this vector.
	*@return number of elements in this vector
	*/
	public int getSize() {
		return this.size;
	}
	
//*****************************************************************************
	
	/**LU Decomposition Back Solve;
	* this method takes the LU matrix and the permutation vector produced by the IMatrix method LUD
	* and solves the equation (LU)*x = b by placing the solution vector x into this vector.
	* This vector should be the same length or longer than b.
	*@param LU The matrix into which the lower and upper decompostions have been placed
	*@param b The b vector in the equation (LU)*x = b
	*@param permutation The row permuations that were necessary to produce the LU matrix parameter
	*/
	public final void LUDBackSolve(IMatrix LU, IVector b, IVector permutation) throws MismatchedSizeException{
		
		if (LU.numRow!=LU.numCol){
			throw (new MismatchedSizeException("LU isn't a square matrix"));
		}
		if (this.size!=LU.getNumRow() || this.size!=permutation.getSize() || this.size!=b.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		int n = LU.getNumRow()-1;
		this.zero();
		
		//Solving the system L*y=b
		IVector y = new IVector(LU.getNumRow());
		int k = (int)permutation.getElement(0);
		y.setElement(0, b.getElement(k));
		for (int i=1; i<=n; i++) {
			double sum = 0;
			for (int j=0; j<=(i-1); j++) {
				sum = sum + LU.getElement(i,j)*y.getElement(j);
			}
			k = (int)permutation.getElement(i);
			y.setElement(i, b.getElement(k) - sum);
		}
		
		//Solving the system U*x=y
		this.setElement(n, y.getElement(n)/LU.getElement(n,n));
		for (int i=n-1; i>=0; i--) {
			double sum = 0;
			for (int j=i+1; j<=n; j++) {
				sum = sum + LU.getElement(i,j)*this.getElement(j);
			}
			this.setElement(i, (y.getElement(i)-sum)/LU.getElement(i,i));
		}
	}
	
//*****************************************************************************
	
	/**Cholesky Decomposition Back Solve;
	* this method takes the L matrix produced by the IMatrix method CholeskyD
	* and solves the equation (L*L.transpose())*x = b by placing the solution vector x into this vector.
	* This vector should be the same length or longer than b.
	*@param L The matrix into which the lower and upper decompostions have been placed
	*@param b The b vector in the equation (LU)*x = b
	*/
	public final void choleskyDBackSolve(IMatrix L, IVector b) throws MismatchedSizeException{
		
		if (L.numRow!=L.numCol){
			throw (new MismatchedSizeException("L isn't a square matrix"));
		}
		if (this.size!=L.getNumRow() || this.size!=b.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		int n = L.getNumRow()-1;
		this.zero();
		
		//Solving the system L*y=b
		IVector y = new IVector(L.getNumRow());
		y.setElement(0 , b.getElement(0)/L.getElement(0,0));
		for (int i=1; i<=n; i++) {
			double sum=0;
			for (int j=0; j<=(i-1); j++)
				sum = sum + L.getElement(i,j) * y.getElement(j);
			y.setElement(i, (b.getElement(i) - sum) / L.getElement(i,i));
		}
		
		//Solving the system L.transpose()*x=y
		L.transpose();
		this.setElement(n, y.getElement(n)/L.getElement(n,n));
		for (int i=n-1; i>=0; i--) {
			double sum = 0;
			for (int j=i+1; j<=n; j++) {
				sum = sum + L.getElement(i,j)*this.getElement(j);
			}
			this.setElement(i, (y.getElement(i)-sum)/L.getElement(i,i));
		}
	}
	
//*****************************************************************************
	
	/**Multiplies matrix m1 times Vector v1 and places the result into this vector (this = m1*v1).
	*@param m1 The matrix in the multiplication
	*@param v1 The vector that is multiplied
	*/
	public final void mul(IMatrix m1, IVector v1) throws MismatchedSizeException{
		
		if (m1.getNumCol() != v1.getSize()) {
			throw (new MismatchedSizeException());
		}
		if (this.size != m1.getNumRow()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<m1.getNumRow(); i++) {
			double sum = 0;
			for (int j=0; j<v1.getSize(); j++)
				sum = sum + m1.getElement(i,j)*v1.getElement(j);
			this.vector[i] = sum;
		}
		
	}
	
//*****************************************************************************
	
	/**Multiplies the transpose of vector v1 (ie, v1 becomes a row vector with respect to the multiplication) times matrix m1 and places the result into this vector (this = transpose(v1)*m1).
	*@param m1 The matrix in the multiplication
	*@param v1 The vector that is temporarily transposed
	*/
	public final void mul(IVector v1, IMatrix m1) throws MismatchedSizeException{
		
		if (m1.getNumRow() != v1.getSize()) {
			throw (new MismatchedSizeException());
		}
		if (this.size != m1.getNumCol()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<v1.getSize(); i++) {
			double sum = 0;
			for (int j=0; j<m1.getNumCol(); j++)
				sum = sum + v1.getElement(j)*m1.getElement(j,i);
			this.vector[i] = sum;
		}
	}
	
//*****************************************************************************
	
	/**Negates the value of this vector: this = -this.*/
	public final void negate() {
		for (int i=0; i<this.size; i++) {
			this.vector[i] = this.vector[i] * -1;
		}
	}
	
//*****************************************************************************
	
	/**Returns the square root of the sum of the squares of this vector (its length in n-dimensional space).
	*@return length of this vector
	*/
	public final double norm() {
		double norm = 0;
		int i;
		for (i=0; i<size; i++)
			norm = norm + Math.pow(this.vector[i], 2);
		return(Math.sqrt(norm));
	}
	
//*****************************************************************************
	
	/**Normalizes this vector in place.*/
	public final void normalize() {
		int i;
		double big;
		big = this.vector[0];
		for (i=1; i<this.size; i++) {
			if (Math.abs(this.vector[i]) > Math.abs(big))
				big = this.vector[i];
		}
		for(i=0; i<this.size; i++)
			this.vector[i] = this.vector[i] / big;
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the normalization of vector v1.
	*@param v1 the un-normalized vector
	*/
	public final void normalize(IVector v1) {
		
		if (this.size != v1.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		int i;
		double big;
		big = this.vector[0];
		for (i=1; i<v1.size; i++) {
			if (Math.abs(v1.getElement(i)) > Math.abs(big))
				big = v1.getElement(i);
		}
		for(i=0; i<this.size; i++)
			this.vector[i] = v1.getElement(i) / big;
	}
	
//*****************************************************************************
	
	/**Returns the sum of the squares of this vector (its length squared in n-dimensional space).
	*@return length squared of this vector
	*/
	public final double normSquared() {
		double norm = 0;
		int i;
		for (i=0; i<size; i++)
			norm = norm + Math.pow(this.vector[i], 2);
		return norm;
	}
	
//*****************************************************************************
	
	/**Scales this vector by the scale factor s.
	*@param s the scalar value
	*/
	public final void scale(double s) {
		for (int i=0; i<this.size; i++) {
			this.vector[i] = this.vector[i] * s;
		}
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the scalar multiplication of the scale factor with the vector v1.
	*@param s the scalar value
	*@param v1 the source vector
	*/
	public final void scale(double s, IVector v1) {
		
		if (this.size != v1.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<this.size; i++) {
			this.vector[i] = v1.getElement(i) * s;
		}
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the scalar multiplication by s of vector v1 plus vector v2 (this = s*v1 + v2).
	*@param s the scalar value
	*@param v1 the vector to be multiplied
	*@param v2 the vector to be added
	*/
	public final void scaleAdd(double s, IVector v1, IVector v2) throws MismatchedSizeException{
		
		if (this.size != v1.getSize()) {
			throw (new MismatchedSizeException());
		}
		if (v1.getSize() != v2.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		IVector aux = new IVector(v1);
		aux.scale(s);
		this.add(aux,v2);
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the values found in the array parameter.
	*@param vector the source array
	*/
	public final void set(double[] vector) {
		for (int i=0; i<this.size; i++) {
			this.vector[i] = vector[i];
		}
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the values found in vector vector.
	*@param vector the source vector
	*/
	public final void set(IVector vector) throws MismatchedSizeException{
		
		if (this.size != vector.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<this.size; i++) {
			this.vector[i] = vector.getElement(i);
		}
	}
	
//*****************************************************************************
	
	/**Modifies the value at the specified index of this vector.
	*@param index the index if the element to modify (zero indexed)
	*@param value the new vector element value
	*/
	public final void setElement(int index, double value) {
		this.vector[index] = value;
	}
	
//*****************************************************************************
	
	/**Changes the size of this vector dynamically.
	* If the size is increased no data values will be lost.
	* If the size is decreased, only those data values whose vector positions were eliminated will be lost.
	*@param length number of desired elements in this vector
	*/
	public final void setSize(int length) {
		double[] aux = new double[this.vector.length];
		for (int i=0; i<this.size; i++) {
			aux[i] = this.vector[i];
		}
		this.vector = new double[length];
		this.size = length;
		for (int i=0; i<length; i++) {
			if (i<aux.length)
				this.vector[i] = aux[i];
			else this.vector[i] = 0;
		}
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the vector difference of itself and vector (this = this - vector).
	*@param vector the other vector
	*/
	public final void sub(IVector vector) throws MismatchedSizeException{
		
		if (this.size != vector.getSize()) {
			throw (new MismatchedSizeException());
		}
		
		for (int i=0; i<this.size; i++) {
			this.vector[i] = this.vector[i] - vector.getElement(i);
		}
	}
	
//*****************************************************************************
	
	/**Sets the value of this vector to the vector difference of vectors vector1 and vector2 (this = vector1 - vector2).
	*@param vector1 the first vector
	*@param vector2 the second vector
	*/
	public final void sub(IVector vector1, IVector vector2) throws MismatchedSizeException{
		if (this.size != vector1.getSize()) {
			throw (new MismatchedSizeException());
		}
		if (vector1.getSize() != vector2.getSize()) {
			throw (new MismatchedSizeException());
		}
		for (int i=0; i<this.size; i++) {
			this.vector[i] = vector1.getElement(i) - vector2.getElement(i);
		}
	}
	
//*****************************************************************************
	
	/**Returns a string that contains the values of this IVector.
	*@return the String representation
	*/
	public String toString() {
		String str = String.valueOf(this.vector[0]);
		for (int i=1; i<this.size; i++) {
			str = str + " " + String.valueOf(this.vector[i]);
		}
		return str;
	}
	
//*****************************************************************************
	
	/**Sets all the values in this vector to zero.*/
	public final void zero() {
		for (int i=0; i<this.size; i++) {
			this.vector[i] = 0;
		}
	}
	
//*****************************************************************************
	
	/**Places the vector values into this column matrix.
	*@return The column matrix containing the vector (but not modified)
	*/
	public final IMatrix toMatrix() {
		IMatrix m = new IMatrix(this.size,1);
		for (int i=0; i<this.size; i++) {
		    m.setElement(i,0,this.getElement(i));
		}
		return m;
	}
	
	/**Places the vector values into a array of doubles.
	*@return The array of doubles containing the vector values (but not modified)
	*/
	public final double[] toDoubleArray() {
		double [] d = new double[this.size];
		for (int i=0; i<this.size; i++)
		{
		    d[i] = this.getElement(i);
		}
		return d;
	}
	
}
